/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.core;

import cn.seaboot.commons.lang.Warning;
import com.google.common.base.Joiner;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.Array;
import java.util.Arrays;

/**
 * 数组拼接，Class和泛型
 *
 * @author Mr.css 2017/8/14 14:54:30
 */
public class ArrayUtils {

    private ArrayUtils() {
    }


    /**
     * 特殊的折半查找，允许 value 值为空，如果值为空，直接返回 -1
     *
     * @param array 数组
     * @param <T>   泛型
     * @return String
     */
    public static <T> int binarySearch(T[] array, @Nullable T v) {
        if(v == null) {
            return -1;
        } else {
            return Arrays.binarySearch(array, v);
        }
    }

    /**
     * 特殊的折半查找，允许 value 值为空，如果值为空，直接返回 -1
     *
     * @param array 数组
     * @param <T>   泛型
     * @return String
     */
    public static <T> int indexOf(T[] array, @Nullable T v) {
        if(v == null) {
            return -1;
        } else {
            for(int i = 0; i < array.length; i++) {
                if(array[i].equals(v)) {
                    return i;
                }
            }
            return -1;
        }
    }

    /**
     * 按照逗号join数组
     *
     * @param array 数组
     * @param <T>   泛型
     * @return String
     */
    public static <T> String join(T[] array) {
        return Joiner.on(',').join(array);
    }

    /**
     * 按照逗号join数组
     *
     * @param array 数组
     * @param <T>   泛型
     * @return String
     */
    public static <T> T nvl(T[] array, int index, T def) {
        return array.length > index ? array[index] : def;
    }

    /**
     * 通过一个泛型数组安全地创建另一个空数组
     * <p>
     * 封装自：{@link Array#newInstance(Class, int)}
     *
     * @param clazz clazz类型
     * @return 新的数组，需要强制转换
     */
    public static Object newArray(Class<?> clazz, int len) {
        if (clazz.isArray()) {
            return ((Object) clazz == (Object) Object[].class) ? new Object[len]
                    : Array.newInstance(clazz.getComponentType(), len);
        } else {
            return Array.newInstance(clazz, len);
        }
    }

    /**
     * 数组合并
     *
     * @param array 数组
     * @param args  任意多个数组
     * @param <T>   要求泛型一致
     * @return 新的数组
     */
    @SafeVarargs
    public static <T> T[] concatAll(T[] array, T[]... args) {
        int len = array.length;
        for (T[] arr : args) {
            len += arr.length;
        }
        T[] result = Arrays.copyOf(array, len);
        int index = array.length;
        for (T[] arr : args) {
            System.arraycopy(arr, 0, result, index, arr.length);
            index += arr.length;
        }
        return result;
    }


    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @param <T>  要求泛型一致
     * @return 新的数组
     */
    public static <T> T[] concat(T[] arr1, T[] arr2) {
        T[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @return 新的数组
     */
    public static boolean[] concat(boolean[] arr1, boolean[] arr2) {
        boolean[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @return 新的数组
     */
    public static short[] concat(short[] arr1, short[] arr2) {
        short[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @return 新的数组
     */
    public static int[] concat(int[] arr1, int[] arr2) {
        int[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @return 新的数组
     */
    public static long[] concat(long[] arr1, long[] arr2) {
        long[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @return 新的数组
     */
    public static float[] concat(float[] arr1, float[] arr2) {
        float[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 数组合并
     *
     * @param arr1 数组
     * @param arr2 元素
     * @return 新的数组
     */
    public static double[] concat(double[] arr1, double[] arr2) {
        double[] result = Arrays.copyOf(arr1, arr1.length + arr2.length);
        System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @param <T>   要求泛型一致
     * @return 新的数组
     */
    @SuppressWarnings(Warning.UNCHECKED)
    public static <T> T[] addAtFirst(T[] array, T t) {
        T[] result = (T[]) newArray(t.getClass(), array.length + 1);
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static boolean[] addAtFirst(boolean[] array, boolean t) {
        boolean[] result = new boolean[array.length + 1];
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static short[] addAtFirst(short[] array, short t) {
        short[] result = new short[array.length + 1];
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static int[] addAtFirst(int[] array, int t) {
        int[] result = new int[array.length + 1];
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static long[] addAtFirst(long[] array, long t) {
        long[] result = new long[array.length + 1];
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static float[] addAtFirst(float[] array, float t) {
        float[] result = new float[array.length + 1];
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static double[] addAtFirst(double[] array, double t) {
        double[] result = new double[array.length + 1];
        result[0] = t;
        System.arraycopy(array, 0, result, 1, array.length);
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @param <T>   要求泛型一致
     * @return 新的数组
     */
    public static <T> T[] addAtLast(T[] array, T t) {
        T[] result = Arrays.copyOf(array, array.length + 1);
        result[array.length] = t;
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static boolean[] addAtLast(boolean[] array, boolean t) {
        boolean[] result = new boolean[array.length + 1];
        System.arraycopy(array, 0, result, 0, array.length);
        result[array.length] = t;
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static short[] addAtLast(short[] array, short t) {
        short[] result = new short[array.length + 1];
        System.arraycopy(array, 0, result, 0, array.length);
        result[array.length] = t;
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static int[] addAtLast(int[] array, int t) {
        int[] result = new int[array.length + 1];
        System.arraycopy(array, 0, result, 0, array.length);
        result[array.length] = t;
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static long[] addAtLast(long[] array, long t) {
        long[] result = new long[array.length + 1];
        System.arraycopy(array, 0, result, 0, array.length);
        result[array.length] = t;
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static float[] addAtLast(float[] array, float t) {
        float[] result = new float[array.length + 1];
        System.arraycopy(array, 0, result, 0, array.length);
        result[array.length] = t;
        return result;
    }

    /**
     * 将一个元素并在数组开头
     *
     * @param t     元素
     * @param array 数组
     * @return 新的数组
     */
    public static double[] addAtLast(double[] array, double t) {
        double[] result = new double[array.length + 1];
        System.arraycopy(array, 0, result, 0, array.length);
        result[array.length] = t;
        return result;
    }


    /**
     * 取出数组中的第一个元素
     * <p>
     *
     * @param arr 数组
     * @param <T> 泛型
     * @return 第一个元素
     */
    public static <T> T getFirst(T[] arr) {
        return CommonUtils.isEmpty(arr) ? null : arr[0];
    }


    /**
     * 取出数组中的最后一个元素
     * <p>
     *
     * @param arr 集合
     * @param <T> 泛型
     * @return 第一个元素
     */
    public static <T> T getLast(T[] arr) {
        return CommonUtils.isEmpty(arr) ? null : arr[arr.length - 1];
    }
}
